//: Playground - noun: a place where people can play

import UIKit

//:  存储属性:只能在类和结构体中存储常量和变量
struct Location {                      // 描述位置的结构体
    var x: Double
    let y: Double
}

var position1 = Location(x: 22.22, y: 33.33) // 变量
position1.x = 20                       // 变量存储属性可以修改
//position1.y = 30                       // ❌, 常量存储属性不可修改

let position2 = Location(x: 20.2, y:30.3 ) // 结构体实例赋值给一个常量
//position2.x = 22.22                    // ❌, 这时变量属性也是不可修改的
// 这是因为结构体属于值类型,当值类型被声明为常量的时候,其所有属性也就成了常量

class Address {                        // 描述地址的类
    let city = "Hangzhou"
    var town: String?
}
let address1 = Address()               // 将一个实例赋值给常量
address1.town = "Yunting"              // 其变量属性仍然可变
// 而当把一个引用类型赋值给一个常量的时候,仍可以修改该实例的变量属性

//:  延迟加载存储属性关键词lazy
class Data {
    var fileName = "data.plist"
    // 此处是比较耗时的数据加载工作
}

class DataManger {
    lazy var data = Data()              // 使用lazy第一次访问的时候才被创建
    // 对数据操作                        // lazy延时加载只能修饰变量,因为常量需要初始值
}

let manager = DataManger()              // 创建实例的时候并没有创建data实例
manager.data                            // 此时才会去执行耗时的数据加载工作

// 如果lazy修饰的属性没有被初始化就被多个线程访问,就会被初始化多次


//: 计算属性:可用于类,结构体,枚举. 不直接存储值,而是提供了一个set和get方法
struct Size {
    var width = 0.0
    var height: Double
    {                // 设置height的get set方法
        get {                           // get方法必须有返回值
            print("get")                // get
            return 10.0                 // height返回10.0
        }
        set {
            width = 20.0                // width设置为 20.0
            print("set")                // set
        }
    }
}
var size1 = Size()
size1.width = 17                       // 设置height值时调用set方法
print(size1.width)                      // 20.0
print(size1.height)                     // 10.0, 获取height值的时候调用get方法

//:  只读属性:只有get方法,没有set方法的属性
struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {                // 必须用var修饰,计算属性值是不固定的
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print(fourByFiveByTwo.volume)           // 40.0, 可以通过点语法访问
//fourByFiveByTwo.volume = 20.0           // ❌, 不能设置新值

//:  属性观察器,willSet, didSet: 只要为属性设置新值就会调用属性观察器
class StepCounter {
    var totalSteps: Int = 0 {           // 一定要设置初始值
        willSet {                       // willSet在新值被设置之前调用
            print(newValue)             // newValue为默认参数名称
        }
        didSet(oldSteps) {              // didSet在新值被设置之后调用
            print(oldSteps)             // 修改默认参数oldValue为oldSteps
        }
        // willSet会将新的属性值传入newValue参数,didSet会将旧的属性值传入oldSteps
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 23             // newValue:23 oldSteps: 0
stepCounter.totalSteps = 5              // newValue:5  oldSteps: 23


// 实例属性: 每创建一个实例,该实例都会拥有一套属于自己的值,实例之间的属性相互独立
// 类型属性: 为类型定义属性时,无论创建多少个该类型的示例,该属性都只有唯一一份

struct someStruct {
    static var typeProperty = "Value"   // 使用static定义类型属性
}

class someClass {
    static var typeProperty = "Value"
    static var computedTypeProperty: Int {
        return 6                        // 可以通过闭包返回属性值
    }
}

print(someStruct.typeProperty)          // Value, 类型属性直接通过其本身来访问
someStruct.typeProperty = "Another value" // typeProperty为var类型,可写
print(someStruct.typeProperty)          // Another value
print(someClass.computedTypeProperty)   // 6


//:  1方法:完成某些特性功能的函数      方法分为需要通过创建实例调用的实例方法和直接可以通过类型名调用的类型方法,结构体.类.枚举中都能定义方法       2实例方法:提供访问和修改实例的属性的方法
class Counter {
    var count = 0                      // 可变属性
    func increment() {                 // 计数器按一递增的实例方法
        count += 1
    }
    func incrementBy(amount: Int) {    // 计数器按指定整数递增的实例方法
        count += amount
    }
    func reset() {                     // 计数器置为0的实例方法
        self.count = 0                 // self可省略,表示当前实例
    }
}
let counter = Counter()                // 创建一个实例
counter.increment()                    // 调用递增1的实例方法
print(counter.count)                   // 1
counter.incrementBy(amount: 4)
print(counter.count)                   // 5
counter.reset()
print(counter.count)                   // 0


//:  结构体和枚举不是引用类型,是值类型,值类型的属性不能直接在实例方法中修改
struct Point {                         // 利用结构体坐标移动
    var x = 0.0, y = 0.0
    // 添加mutating关键字可以从方法内部改变结构体的属性
    mutating func movedByX(deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.movedByX(deltaX: 2.0, y: 3.0) // 该方法是修改了这个点,而不是返回新的点
print("x:\(somePoint.x),y:\(somePoint.y)")

let fixedPoint = Point(x: 2.0, y: 4.0)
//fixedPoint.movedByX(deltaX: 2.0, y: 2.0) // ❌, 常量结构体类型的属性不能被改变


//:  self代表自身,在可变方法中给self赋值
struct NewPoint {                      // 利用结构体坐标移动
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        self = NewPoint(x: x + deltaX, y: y + deltaY)
    }
}
var someNewPoint = NewPoint(x: 2, y: 3)
someNewPoint.moveByX(deltaX: 3, y: 5)  // 正方向x加3.0,y加5.0
print(someNewPoint)                // NewPoint(x: 5.0, y: 8.0)

enum StateSwitch {
    case Off, Low, High                // 定义三种状态
    // 枚举也是值类型,改变其属性也要加mutating
    mutating func next() {             // 每次next都可以切换不同状态
        switch self {
        case .Off:
            self = .Low
        case .Low:
            self = .High
        case .High:
            self = .Off
        }
    }
}
var ovenLight = StateSwitch.Low        // Low
ovenLight.next()                       // High
ovenLight.next()                       // Off

//:  类方法:直接通过类名本身调用来方法,可以是类,结构体,枚举
enum SomeEnum {
    static func printSome() {          // 枚举中的类型方法
        print("SomeEnum")              // SomeEnum
    }
}
SomeEnum.printSome()

struct SomeStruct {
    static func printSome() {          // 结构体中的类型方法
        print("SomeStruct")            // SomeStruct
    }
}
SomeStruct.printSome()

class Level {
    static var commonLevel = 1         // 公共等级
    // 通过添加class关键字变成类方法,该方法允许子类继承
    class func addOneLevel() {         // 公共等级加1
        commonLevel += 1
    }
    // 通过添加static关键字变成类方法
    static func addLevelBy(level: Int) {
        commonLevel += level
    }
    
    var currentLevel = 1               // 实例等级
    // 实例方法
    func MaxLevel() -> Int {           // 比较后返回大的等级数
        if currentLevel >= Level.commonLevel {
            return currentLevel
        }else {
            return Level.commonLevel
        }
    }
}
Level.addOneLevel()
Level.addLevelBy(level: 3)
print(Level.commonLevel)               // 5

let player1 = Level()
player1.currentLevel = 2
print(player1.MaxLevel())              // 5

let player2 = Level()
player2.currentLevel = 6
print(player2.MaxLevel())              // 6



